/**
 * 此示例演示如何CheerioCrawler从外部文件中抓取 URL 列表、使用纯 HTTP 请求加载每个 URL、使用Cheerio 库解析 HTML并从中提取一些数据：页面标题和所有h1标签。
 */

import { Dataset, CheerioCrawler, log, LogLevel } from 'crawlee';

// 爬虫程序标有各种实用程序，例如用于日志记录。这里我们使用日志记录的调试级别来改进调试体验。此功能是可选的！
log.setLevel(LogLevel.DEBUG);

// 创建CheerioCrawler类的一个实例——一个爬网程序,它自动加载URL并使用cheerio库解析它们的HTML。
const crawler = new CheerioCrawler({
    // 爬虫以家具方式下载和处理网页,根据可用的系统内存和CPU自动管理（请参阅AutoscaledPool类）。在这里，我们定义了一些可变性的硬限制。
    minConcurrency: 10,
    maxConcurrency: 50,

    // 出现错误时，每个页面最多重试一次。
    maxRequestRetries: 1,

    // 增加每个页面处理的超时时间。
    requestHandlerTimeoutSecs: 30,

    // 每次抓取限制为 10 个请求
    maxRequestsPerCrawl: 10,

    // 每个要爬取的URL都会调用这个函数。
    // 它接受一个参数，这是一个带有选项的对象：
    // https://crawlee.dev/api/cheerio-crawler/interface/CheerioCrawlerOptions#requestHandler
    // 我们仅使用其中 2 个进行演示：
    // - request：Request类的实例，包含正在爬取的URL和HTTP方法等信息
    // - $: 包含已解析 HTML 的 Cheerio 对象
    async requestHandler({ request, $ }) {
        log.debug(`处理 ${request.url}...`);

        // 使用cheerio 从页面中提取数据。
        const title = $('title').text();
        const h1texts: { text: string }[] = [];
        $('h1').each((_index, el) => {
            h1texts.push({
                text: $(el).text(),
            });
        });

        // 将结果存储到数据集。在本地配置中，数据将作为 JSON 文件存储在 ./storage/datasets/default 中
        await Dataset.pushData({
            url: request.url,
            title,
            h1texts,
        });
    },

    // 如果页面处理失败超过 maxRequestRetries + 1 次，则调用此函数。
    failedRequestHandler({ request }) {
        log.debug(`请求 ${request.url} 两次已失败.`);
    },
});

// 运行爬虫并等待其完成。
await crawler.run(['https://crawlee.dev']);

log.debug('爬虫完成!');
